#ifndef	ACL_DBPOOL_INCLUDE_H
#define	ACL_DBPOOL_INCLUDE_H

#ifdef	__cplusplus
extern "C" {
#endif

#include "../stdlib/acl_define.h"
#include <time.h>
#include "../stdlib/acl_iterator.h"

#define	ACL_DB_DEBUG_MEM	(1<<0)

#ifndef HAS_MYSQL
#define HAS_MYSQL          // 如果连接池是连接的MySQL打开宏定义
#endif

typedef struct ACL_DB_HANDLE ACL_DB_HANDLE;
typedef struct ACL_SQL_RES ACL_SQL_RES;
typedef struct ACL_DB_POOL ACL_DB_POOL;

/**
 * 连接池信息
 */
typedef struct ACL_DB_INFO {
	int   db_max;		    /* 连接池最大连接数 */
	char  db_addr[256];	    /* 数据库服务地址 */
	char  db_name[256];	    /* 数据库名称 */
	char  db_user[256];	    /* 数据库帐号 */
	char  db_pass[256];	    /* 帐号密码 */
	unsigned long db_flags;	/* (mysql) 连接标志位 */
	int   ping_inter;	    /* 探测数据库连接的时间间隔 */
	int   timeout_inter;	/* 数据库连接的空闲超时时间 */
	int   auto_commit;	    /* (mysql) 是否启用自动提交过程 */
	int   conn_timeout;	    /* (mysql/null) 连接超时时间 */
	int   rw_timeout;	    /* (mysql/null) IO读写超时时间 */
	int   buf_size;		    /* (null) IO缓冲区大小 */
	int   debug_flag;	    /* 调试标志位 */

	/* 在真实连接数据库之前/后调用用户设置的回调函数, 此项可以设为 NULL
	 *
	 * 例如db_type传入mysql，__open_mysql_handle中使用, 在句柄和数据库建立连接mysql_real_connect的前后使用这两个函数
	 *
	 * 如果 db_before_connect/db_after_connect 返回 < 0 则会导致
	 * acl_dbpool_peek 返回 NULL
	 */
	int  (*db_before_connect)(ACL_DB_HANDLE* db_handle, void *ctx);
	int  (*db_after_connect)(ACL_DB_HANDLE* db_handle, void *ctx);

	void *ctx;		         /* db_before_connect/db_after_connect 参数之一 */
} ACL_DB_INFO;

/**
 * 数据库连接句柄
 */
struct ACL_DB_HANDLE {
	#define	ACL_DBH_STATUS_NULL  	0       // 该句柄尚未建立连接
	#define	ACL_DBH_STATUS_READY	1       // 该句柄有连接且空闲(无使用)
	#define	ACL_DBH_STATUS_INUSE	2       // 该句柄有连接且正在被使用

	int status;
	time_t timeout;
	time_t ping;

	ACL_DB_POOL *parent;

	// acl_dbsql中声明定义的数据库连接句柄使用的操作语句
	ACL_SQL_RES *(*sql_select)(ACL_DB_HANDLE *handle, const char *sql, int *error);

	int (*sql_update)(ACL_DB_HANDLE *handle, const char *sql, int *error);

	void (*free_result)(ACL_SQL_RES *res);

	int (*sql_results)(ACL_DB_HANDLE *handle, const char *sql, int *error,
			int (*walk_fn)(const void** result_row, void *arg), void *arg);

	int (*sql_result)(ACL_DB_HANDLE *handle, const char *sql, int *error,
			int (*callback)(const void** result_row, void *arg), void *arg);
};

/**
 * sql操作结果集
 */
struct ACL_SQL_RES {
	void *res;              // sql查询结果集（可以从中取出字段）
	int  num;               // 查询结果总行数（不包括字段名）

	const char **fields;    // add by qbyao 用户查询列名
	int  fields_num;        // add by qbyao 列数

    /* for acl_iterator */
	/* 取迭代器头函数 */
	const void *(*iter_head)(ACL_ITER*, struct ACL_SQL_RES*);
	/* 取迭代器下一个函数 */
	const void *(*iter_next)(ACL_ITER*, struct ACL_SQL_RES*);
	/* add by qbyao 取迭代器关联的当前容器成员结构对象 */
	const void *(*iter_info)(ACL_ITER*, struct ACL_SQL_RES*);
};

/**
 * 数据库连接池
 */
struct ACL_DB_POOL {
	ACL_DB_INFO db_info;                           // 在创建数据库连接池时将用户传入的db_info拷贝到此处

	ACL_DB_HANDLE *(*dbh_peek)(ACL_DB_POOL *);     // 从连接池中获取一个连接句柄回调函数
	void  (*dbh_check)(ACL_DB_POOL *);             // 手工检查连接池的每个连接回调函数
	void  (*dbh_release)(ACL_DB_HANDLE *);         // 将数据库连接句柄释放给数据库连接池回调函数
	void *(*dbh_export)(ACL_DB_HANDLE *);          // 将数据库连接转换为实际的数据库连接句柄回调函数
	void  (*dbh_close)(ACL_DB_HANDLE *);           // 当使用者自己检测到该数据库连接出错时，可以通过此接口强行关闭该连接
	int   (*dbh_ping)(ACL_DB_HANDLE *);
	void  (*destroy)(ACL_DB_POOL *);               // 销毁一个数据库连接池回调函数

	int  db_max;                                   // 连接池最大连接数，与db_info中该字段保持一致
	int  db_ready;                                 // ACL_DBH_STATUS_READY状态句柄个数
	int  db_inuse;                                 // ACL_DBH_STATUS_INUSE状态句柄个数
};

/*----------------------------------------------------------------------------*/
// 获得连接池的auto_commit设定值
#define	ACL_DB_AUTO_COMMIT(_db_pool_) (_db_pool_ ? _db_pool_->db_info.auto_commit : 0)

/* in acl_dbpool.c */
/**
 * 创建一个数据库连接池，按照db_type要求调用具体的连接池创建函数，例如db_type传入mysql，调用acl_dbpool_mysql.acl_dbpool_mysql_create
 *
 * 只是分配了连接池对象，没有实际创建任何句柄，指定好各个连接池操作函数，在acl_dbpool_peek的过程中按需创建句柄并打开连接
 *
 * @param db_type {const char*} 数据库类型名, 目前默认支持 mysql
 * @param db_info {const ACL_DB_INFO*} 记录着有关连接数据所需要的信息
 * @return {ACL_DB_POOL*} 一个数据库连接池
 */
ACL_API ACL_DB_POOL *acl_dbpool_create(const char *db_type, const ACL_DB_INFO *db_info);

/**
 * 销毁一个数据库连接池，例如db_type传入mysql，调用acl_dbpool_mysql.__dbpool_mysql_destroy（释放句柄连接，句柄空间，acl_pthread_mutex_destroy，句柄池空间，连接池对象）
 * @param db_pool 数据库连接池句柄
 */
ACL_API void acl_dbpool_destroy(ACL_DB_POOL *db_pool);

/**
 * 从连接池中获取一个连接句柄(状态变为ACL_DBH_STATUS_INUSE)，例如db_type传入mysql，调用acl_dbpool_mysql.__dbpool_mysql_peek
 *
 * （如果在句柄池中找到ACL_DBH_STATUS_READY状态的句柄直接返回并置为ACL_DBH_STATUS_INUSE）
 * 调用__open_mysql_handle（如果在句柄池中没有找到ACL_DBH_STATUS_READY状态的句柄但是有ACL_DBH_STATUS_NULL状态的句柄，为其创建连接并修改为ACL_DBH_STATUS_INUSE）
 * 调用__open_mysql_handle（如果在句柄池中没有找到ACL_DBH_STATUS_READY和ACL_DBH_STATUS_NULL状态的句柄，分配新的句柄加入句柄池，为其创建连接并修改为ACL_DBH_STATUS_INUSE）
 *
 * @param db_pool {ACL_DB_POOL*} 数据库连接池句柄
 * @return {ACL_DB_HANDLE*} 数据库连接句柄，如果为空则表示出错或连接池已满
 */
ACL_API ACL_DB_HANDLE *acl_dbpool_peek(ACL_DB_POOL *db_pool);

/**
 * 将数据库连接句柄释放给数据库连接池，句柄从ACL_DBH_STATUS_INUSE状态改成ACL_DBH_STATUS_READY状态       (归还)
 *
 * 例如db_type传入mysql，调用acl_dbpool_mysql.__dbpool_mysql_release
 *
 * @param db_handle {ACL_DB_HANDLE*} 数据库连接句柄
 */
ACL_API void acl_dbpool_release(ACL_DB_HANDLE *db_handle);

/**
 * 当使用者自己检测到该数据库连接出错时，可以通过此接口强行关闭该连接， 例如db_type传入mysql，调用acl_dbpool_mysql.__dbpool_mysql_close  (断开连接+归还)
 *
 * 完成mysql_close(mysql_handle->connection)，并将句柄状态改成ACL_DBH_STATUS_NULL
 *
 * @param db_handle {ACL_DB_HANDLE*} 数据库连接句柄
 */
ACL_API void acl_dbpool_close(ACL_DB_HANDLE *db_handle);

/**
 * 手工检查连接池的每个连接一般连接池内部会定期检查每个连接（句柄断开连接、句柄ping服务器），也可以通过此函数手工进行强制检查
 * 例如db_type传入mysql，调用acl_dbpool_mysql.__dbpool_mysql_check
 * @param db_pool {ACL_DB_POOL*} 数据库连接池句柄
 */
ACL_API void acl_dbpool_check(ACL_DB_POOL *db_pool);

/**
 * 从数据库句柄中取出实际的connection，例如db_type传入mysql，调用acl_dbpool_mysql.__dbpool_mysql_export得到MYSQL* connection
 * @param db_handle {ACL_DB_HANDLE*} 数据库连接句柄
 * @return void * 使用者需要将其强制转换为自己所用的数据库连接引擎
 */
ACL_API void *acl_dbpool_export(ACL_DB_HANDLE *db_handle);

/**
 * 设置连接池的定时PING处理函数，如果不设置此值则内部采用缺省方式
 * @param db_pool {ACL_DB_POOL*} 数据库连接池句柄
 * @param ping_fn {int (*)(ACL_DB_HANDLE*)} 探测连接状态的函数指针
 */
ACL_API void acl_dbpool_set_ping(ACL_DB_POOL *db_pool, int (*ping_fn)(ACL_DB_HANDLE*));
/*----------------------------------------------------------------------------*/

#ifdef	__cplusplus
}
#endif

#endif
